home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet multimedia / Grafika i zdjecia / Edytory grafiki rastrowej i wektorowej / Inscape 0.44.1 / Inkscape-0.44.1-1.win32.exe / doc / architecture.txt < prev    next >
Text File  |  2006-09-06  |  20KB  |  491 lines

  1. Sodipodi internal architecture
  2.  
  3. 1. Overview
  4.  
  5. The Sodipodi display and editing engine is built using the
  6. "Model-View-Controller" (MVC) paradigm.  Unlike "classic" MVC
  7. programs, we have further split model into two distinct layers,
  8. 'backbone' and 'document'. This has proven to be extremely powerful
  9. technique, giving us clear and fast implementation, functional
  10. granularity and easy expandibility.
  11.  
  12. 1.1. Agnostic XML backbone
  13.  
  14. The basis of the sodipodi document is its plain XML representation in
  15. memory. This is a tree-shaped structure, in which each node is
  16. represented by a lightweight typeless object (SPRepr). These objects
  17. implement a minimal interface of both control (methods) and mutation
  18. events (callbacks).  We use the term 'agnostic' for describing that
  19. part of model, to underline the typeless nature of SPRepr. More or
  20. less, this is just an XML file representation in memory.
  21.  
  22. 1.2. Typed SVG document
  23.  
  24. The most actively used part of the sodipodi document model is the SVG
  25. object tree. This is constructed on top of the XML tree, and reacts to
  26. all mutation events in the agnostic tree, thus always keeping its
  27. internal state synchronized with the backbone. The opposite is not
  28. true - the XML backbone is not aware of the SVG object tree, and thus
  29. does not react to its modifications. If writeback to the backbone is
  30. needed, it must be requested explicitly by the controller.  The SVG
  31. tree is constructed of SPObject subclasses - in general there is one
  32. subclass for each SVG element type, plus abstract base classes.
  33.  
  34. 1.3. NRArena view
  35.  
  36. NRarena is an abstract display engine that allows construction of
  37. 'display caches' from NRArenaItem subclasses. These are lightweight,
  38. having only some basic object types, and used for most of the display
  39. needs of Sodipodi.  Both the editing window, and the bitmap export
  40. code create special NRArena instances, and ask the SVG document to
  41. show itself to the given NRArena.  There is a ::show virtual method,
  42. implemented by all visible object classes, that adds an NRArenaItem
  43. node to the display tree. The completed display cache is used for fast
  44. screen updates and stripe based bitmap exports.  During the NRArena
  45. lifetime SVG objects keep all of the display cache elements constantly
  46. updated, thus ensuring the display is always up to date.
  47.  
  48. 1.4. Controllers
  49.  
  50. Like the model suggests, controllers can be implemented acting on
  51. different layers.  Which one is best depends on the type of action
  52. that the given controller performs. Usually very generic and
  53. single-shot operating controllers act on the SPRepr layer, while those
  54. providing visual feedback or tied to a certain object type act on the
  55. SPObject layer.
  56.  
  57.  
  58.  
  59. 2. Detailed view
  60.  
  61. 2.1. SPRepr
  62.  
  63. The most basic SVG (XML) document backbone is implemented as an
  64. in-memory tree of SPRepr objects, each object corresponding to a
  65. single node in the XML file.  Currently there are only two types of
  66. SPReprs - normal element nodes and text nodes.  More types may be
  67. added in the future, but the structure will probably always remain
  68. much simpler (and faster) than DOM.
  69.  
  70. SPRepr may have:
  71. - attributes (keyword/value) pairs
  72. - content (text)
  73. - child nodes
  74.  
  75. Attribute values are textual, and no checks are performed in that
  76. layer to ensure document validity. Also, CSS style strings are
  77. unparsed in that layer.  The SPRepr tree is built during document
  78. loading or creation. As it is textual and always synchronized with the
  79. display, unfiltered saving involves just dumping it into a file.
  80.  
  81. The basic API acting on SPRepr level is really spartan.
  82.  
  83. SPRepr *sp_repr_new (const unsigned char *name)
  84. SPRepr *sp_repr_new_text (const unsigned char *content)
  85. SPRepr *sp_repr_ref (SPRepr *repr)
  86. SPRepr *sp_repr_unref (SPRepr *repr)
  87. SPRepr *sp_repr_duplicate (SPRepr *repr)
  88.  
  89. int sp_repr_set_content (SPRepr *repr, const unsigned char *content)
  90. int sp_repr_set_attr (SPRepr *repr, const unsigned char *key, const unsigned char *value)
  91. int sp_repr_add_child (SPRepr *repr, SPRepr *child, SPRepr *ref)
  92. int sp_repr_remove_child (SPRepr *repr, SPRepr *child)
  93. int sp_repr_change_order (SPRepr *repr, SPRepr *child, SPRepr *ref)
  94.  
  95. In addition there are some accessor methods and lot of convenience ones.
  96.  
  97. Each SPRepr can have one or many event vectors associated with it.
  98. Event vector is a block of callback pointers for different kind of
  99. mutation events.
  100.  
  101. void sp_repr_add_listener (SPRepr *repr, const SPReprEventVector *vector, void *data)
  102. void sp_repr_remove_listener_by_data (SPRepr *repr, void *data)
  103.  
  104. struct _SPReprEventVector {
  105.         void (* destroy) (SPRepr *repr, gpointer data);
  106.         gboolean (* add_child) (SPRepr *repr, SPRepr *child, SPRepr *ref, gpointer data);
  107.         void (* child_added) (SPRepr *repr, SPRepr *child, SPRepr *ref, gpointer data);
  108.         gboolean (* remove_child) (SPRepr *repr, SPRepr *child, SPRepr *ref, gpointer data);
  109.         void (* child_removed) (SPRepr *repr, SPRepr *child, SPRepr *ref, gpointer data);
  110.         gboolean (* change_attr) (SPRepr *repr, const guchar *key, const guchar *oldval, const guchar *newval, gpointer data);
  111.         void (* attr_changed) (SPRepr *repr, const guchar *key, const guchar *oldval, const guchar *newval, gpointer data);
  112.         gboolean (* change_content) (SPRepr *repr, const guchar *oldcontent, const guchar *newcontent, gpointer data);
  113.         void (* content_changed) (SPRepr *repr, const guchar *oldcontent, const guchar *newcontent, gpointer data);
  114.         gboolean (* change_order) (SPRepr *repr, SPRepr *child, SPRepr *oldref, SPRepr *newref, gpointer data);
  115.         void (* order_changed) (SPRepr *repr, SPRepr *child, SPRepr *oldref, SPRepr *newref, gpointer data);
  116. }
  117.  
  118. All events, except destroys (which are unconditional), have pre- and
  119. post- event callbacks.  The pre-event callback's return value is used to
  120. signal whether the given modification is allowed. If it is FALSE the
  121. operation will be cancelled and the invoking method will also return
  122. FALSE.  Using callbacks in block is much more convenient than adding
  123. them one-by-one, as the listening code usually wants to install several
  124. handlers at once, and the same set of handlers for many different
  125. nodes. NULL pointers are allowed in event vector.
  126.  
  127. Although the most important functionality of the SPRepr tree is to
  128. serve as a document backbone, it has other functions besides
  129. that. SPReprs are also used to store preferences, the copy buffer and
  130. the undo stack.
  131.  
  132.  
  133.  
  134. 2.2. SPObject
  135.  
  136. SPObject is an abstract base class of all of the document nodes at the
  137. SVG document level. Each SPObject subclass implements a certain SVG
  138. element node type, or is an abstract base class for different node
  139. types.  The SPObject layer is bound to the SPRepr layer, closely
  140. following the SPRepr mutations via callbacks.  During creation,
  141. SPObject parses and interprets all textual attributes and CSS style
  142. strings of the SPRepr, and later updates the internal state whenever
  143. it receives a signal about a change. The opposite is not true - there
  144. are methods manipulating SPObjects directly and such changes do not
  145. propagate to the SPRepr layer. This is important for implementation of
  146. the undo stack, animations and other features.
  147.  
  148. SPObjects are bound to the higher-level container SPDocument, which
  149. provides document level functionality such as the undo stack,
  150. dictionary and so on.
  151.  
  152. SPObjects are implemented using the Gtk object system (GObjects in gtk
  153. 2 version), which provides an extremely powerful and flexible OO
  154. framework in pure C.
  155.  
  156. SPObject class hierarchy
  157.  
  158. SPObject ABSTRACT
  159.   SPObjectGroup ABSTRACT
  160.     SPNamedView <sodipodi:namedview>
  161.     SPClipPath <clipPath>
  162.   SPGuide <sodipodi:guide>
  163.   SPPaintServer ABSTRACT
  164.     SPGradient ABSTRACT
  165.       SPLinearGradient <linearGradient>
  166.       SPRadialGradient <radialGradient>
  167.     SPPattern <pattern>
  168.   SPDefs <defs>
  169.   SPItem ABSTRACT
  170.     SPGroup <g>
  171.       SPRoot <svg>
  172.       SPAnchor <a>
  173.   SPImage <image>
  174.   SPPath ABSTARCT
  175.     SPShape <path>
  176.       SPLine <line>
  177.       SPPolyLine <polyline>
  178.       SPPolygon <polygon>
  179.         SPStar <sodipodi:star>
  180.       SPRect <rect>
  181.       SPSpiral <sodipodi:spiral>
  182.       SPGenericEllipse ABSTRACT
  183.         SPCircle <circle>
  184.         SPEllipse <ellipse>
  185.     SPArc <sodipodi:arc>
  186.   SPChars ABSTRACT
  187.     SPString TEXT NODE
  188.   SPDefs <defs>
  189.   SPText <text>
  190.   SPTSpan <tspan>
  191.  
  192. SPObject internals
  193.  
  194. struct _SPObject {
  195.     GtkObject object;
  196.     unsigned int hrefcount;
  197.     SPDocument *document;
  198.     SPObject *parent;
  199.     SPObject *next;
  200.     SPRepr *repr;
  201.     unsigned char *id;
  202.     SPStyle *style;
  203.     const unsigned char *title;
  204.     const unsigned char *description;
  205. };
  206.  
  207. The basic refcounting is handled by the parent class
  208. (GtkObject). Hrefcount is used for weak references, for example, to
  209. determine whether any graphical element references a certain gradient
  210. node.  The parent and next fields are used to establish the tree
  211. structure.  Id is copy of the SPRepr 'id' attribute for normal nodes,
  212. and is used as a unique index of all objects in the given document.
  213.  
  214. Virtual methods
  215.  
  216. /******** Disclaimer *******/
  217. This will change a lot in the future
  218.  
  219. void ::build (SPObject *object, SPDocument *document, SPRepr *repr)
  220.  
  221. This has to be invoked immediately after creation of an SPObject. The
  222. frontend method ensures that the new object is properly attached to
  223. the document and repr; implementation then will parse all of the attributes,
  224. generate the children objects and so on.  Invoking ::build on the SPRoot
  225. object results in creation of the whole document tree (this is, what
  226. SPDocument does after the creation of the XML tree).
  227.  
  228. void ::release (SPObject *object)
  229.  
  230. This is the opposite of ::build. It has to be invoked as soon as the
  231. object is removed from the tree, even if it is still alive according
  232. to reference count. The frontend unregisters the object from the
  233. document and releases the SPRepr bindings; implementations should free
  234. state data and release all child objects.  Invoking ::release on
  235. SPRoot destroys the whole document tree.
  236.  
  237. void ::child_added (SPObject *object, SPRepr *child, SPRepr *ref)
  238. void ::remove_child (SPObject *object, SPRepr *child)
  239. void ::order_changed (SPObject *object, SPRepr *repr, SPRepr *oldref, SPRepr *newref)
  240.  
  241. These are invoked whenever the given mutation event happens in the XML
  242. tree.  ::remove_child is invoked BEFORE removal from the XML tree
  243. happens, so grouping objects can safely release the child data.  The
  244. other two will be invoked AFTER the actual XML tree mutation.  Only
  245. grouping objects have to implement these.
  246.  
  247. void ::read_attr (SPObject *object, const unsigned char *key)
  248.  
  249. Signals object that the XML attribute is changed. The frontend checks
  250. for 'id' attribute; implementations have to read the actual attribute
  251. value and update the internal state.
  252.  
  253. void ::read_content (SPObject *object)
  254.  
  255. Signals object that the XML node content has changed. Only meaningful for
  256. SPString implementing XML TEXT node.
  257.  
  258. void ::modified (SPObject *object, unsigned int flags)
  259.  
  260. Virtual method AND signal implementing asynchronous state change
  261. notification. Whenever the object internal state changes, it requests
  262. that ::modified will be scheduled from the idle loop.  Flags are given
  263. as hints as to what exactly changes. Read the relevant section for
  264. more information.
  265.  
  266. SPRepr ::write (SPObject *object, SPRepr *repr, unsigned int flags)
  267.  
  268. Requests SPObject internal state to be written back to the SPRepr. If
  269. the SP_OBJECT_WRITE_BUILD flag is set, SPRepr is created, if necessary.
  270. This is used at various places, most notably to generate a plain SVG
  271. document, and to complete certain GUI operations.
  272.  
  273.  
  274.  
  275. 2.3. SPItem
  276.  
  277. SPItem is an abstract base class for all graphic (visible) SVG nodes. It
  278. is a subclass of SPObject, with great deal of specific functionality.
  279.  
  280. SPItem internals
  281.  
  282. struct _SPItem {
  283.         SPObject object;
  284.         unsigned int sensitive : 1;
  285.         unsigned int stop_paint: 1;
  286.         double affine[6];
  287.         SPItemView *display;
  288.         SPClipPath *clip;
  289. };
  290.  
  291. Affine is a 3x2 matrix describing transformation from the item to the
  292. parent local coordinate systems. Each display in linked list has a link
  293. to a single NRArenaItem that implements actual renderable image of
  294. the item.
  295.  
  296. Virtual methods
  297.  
  298. /******** Disclaimer *******/
  299. This will change a lot in the future
  300. Only the most important are listed
  301.  
  302. void ::bbox (SPItem *item, ArtDRect *bbox, const double *transform)
  303.  
  304. Calculates item's logical bounding box.  The logical bbox does not
  305. take into account the stroke width, nor certain other visual
  306. properties. Transformation is a 3x2 matrix describing coordinate
  307. transform from the item's local coordinate system to the coordinate
  308. system of the bounding box.
  309.  
  310. void ::print (SPItem *item, SPPrintContext *ctx)
  311.  
  312. Prints the item's visual representation, using the internal printing
  313. frontend.  In the future this may be turned into a more generic
  314. exporting method.
  315.  
  316. char ::description (SPItem *item)
  317.  
  318. Gives a short description of the item suitable for use in a statusbar,
  319. etc. 
  320.  
  321. NRArenaItem ::show (SPItem *item, NRArena *arena)
  322.  
  323. Creates an NRArena display cache representation of the item. The
  324. frontend places the generated item into a hierarchy; implementations
  325. have to build a correct NRArenaItem and keep it up to date.
  326.  
  327. void (* hide) (SPitem *item, NRArena *arena)
  328.  
  329. The opposite of ::show.
  330.  
  331. void ::write_transform (SPItem *item, SPRepr *repr, double *transform)
  332.  
  333. Tries to remove the extra transformation by modifying other aspects of
  334. the item representation.  For example, by changing the rectangle width
  335. and height, the scaling component of the transformation can be
  336. dropped.  This is used to make the SVG file more human-readable.
  337.  
  338. void ::menu (SPItem *item, SPDesktop *desktop, GtkMenu *menu)
  339.  
  340. Appends item specific lines into the menu. This is used to generate
  341. the context menu, and will probably move into a separate module in 
  342. the future.
  343.  
  344.  
  345.  
  346. 2.4 SPDocument
  347.  
  348. SPDocument serves as the container of both model trees (agnostic XML
  349. and typed object tree), and implements all of the document-level
  350. functionality used by the program.
  351.  
  352. SPDocument implements undo and redo stacks and an id-based object
  353. dictionary.  Thanks to unique id attributes, the latter can be used to
  354. map from the XML tree back to the object tree.  Documents are
  355. themselves registered by the main program metaobject 'Sodipodi', which
  356. does elementary bookeeping.
  357.  
  358. SPDocument performs the basic operations needed for asynchronous
  359. update notification (SPObject ::modified virtual method), and implements
  360. the 'modified' signal, as well.
  361.  
  362. Many document level operations, like load, save, print, export and so on,
  363. use SPDocument as their basic datatype.
  364.  
  365. 2.4.1. Undo and Redo implementation
  366.  
  367. Using the split document model gives sodipodi a very simple and clean
  368. undo implementation. Whenever mutation occurs in the XML tree,
  369. SPObject invokes one of the five corresponding handlers of its
  370. container document. This writes down a generic description of the
  371. given action, and appends it to the recent action list, kept by the
  372. document. There will be as many action records as there are mutation
  373. events, which are all kept and processed together in the undo
  374. stack. Two methods exist to indicate that the given action is completed:
  375.  
  376. void sp_document_done (SPDocument *document)
  377. void sp_document_maybe_done (SPDocument *document, const unsigned char *key)
  378.  
  379. Both move the recent action list into the undo stack and clear the
  380. list afterwards.  While the first method does an unconditional push,
  381. the second one first checks the key of the most recent stack entry. If
  382. the keys are identical, the current action list is appended to the
  383. existing stack entry, instead of pushing it onto its own.  This
  384. behaviour can be used to collect multi-step actions (like winding the
  385. Gtk spinbutton) from the UI into a single undoable step.
  386.  
  387. For controls implemented by Sodipodi itself, implementing undo as a
  388. single step is usually done in a more efficent way. Most controls have
  389. the abstract model of grab, drag, release, and change user
  390. action. During the grab phase, all modifications are done to the
  391. SPObject directly - i.e. they do not change XML tree, and thus do not
  392. generate undo actions either.  Only at the release phase (normally
  393. associated with releasing the mousebutton), changes are written back
  394. to the XML tree, thus generating only a single set of undo actions.
  395.  
  396.  
  397. 2.5. SPView and SPviewWidget
  398.  
  399. SPView is an abstract base class of all UI document views.  This
  400. includes both the editing window and the SVG preview, but does not
  401. include the non-UI RGBA buffer-based NRArenas nor the XML editor or
  402. similar views.  The SPView base class has very little functionality of
  403. its own.
  404.  
  405. SPViewWidget is a GUI widget that contain a single SPView. It is also
  406. an abstract base class with little funtionality of its own.
  407.  
  408. 2.6. SPDesktop
  409.  
  410. SPDesktop is a subclass of SPView, implementing an editable document
  411. canvas.  It is extensively used by many UI controls that need certain
  412. visual representations of their own.
  413.  
  414. SPDesktop provides a certain set of SPCanvasItems, serving as GUI
  415. layers of different control objects. The one containing the whole
  416. document is the drawing layer. In addition to it, there are grid,
  417. guide, sketch and control layers. The sketch layer is used for
  418. temporary drawing objects, before the real objects in document are
  419. created. The control layer contains editing knots, rubberband and
  420. similar non-document UI objects.
  421.  
  422. Each SPDesktop is associated with a SPNamedView node of the document
  423. tree.  Currently, all desktops are created from a single main named
  424. view, but in the future there may be support for different ones.
  425. SPNamedView serves as an in-document container for desktop-related
  426. data, like grid and guideline placement, snapping options and so on.
  427.  
  428. Associated with each SPDesktop are the two most important editing
  429. related objects - SPSelection and SPEventContext.
  430.  
  431. Sodipodi keeps track of the active desktop and invokes notification
  432. signals whenever it changes. UI elements can use these to update their
  433. display to the selection of the currently active editing window.
  434.  
  435. 2.7. SPSelection
  436.  
  437. This is a per-desktop object that keeps the list of selected objects
  438. at the given desktop. Both SPItem and SPRepr lists can be retrieved
  439. from the selection. Many actions operate on the selection, so it is
  440. widely used throughout the Sodipodi code.
  441.  
  442. SPSelection also implements its own asynchronous notification signals,
  443. that UI elements can listen to.
  444.  
  445. 2.8. SPEventContext
  446.  
  447. SPEventContext is an abstract base class of all tools. As the name
  448. indicates, event context implementations process UI events (mouse
  449. movements and keypresses) and take actions (like creating or modifying
  450. objects).  There is one event context implementation for each tool,
  451. plus few abstract base classes. Writing a new tool involves
  452. subclassing SPEventContext.
  453.  
  454.  
  455.  
  456. 3. Some thoughts
  457.  
  458. 3.1. Why do we need a two-level model tree?
  459.  
  460. The need for a typed object tree is obvious if we want to utilize OO
  461. programming - which we certainly want to do. Although implemented in pure C,
  462. Sodipodi uses the gtk (glib in future versions) type and object system,
  463. which gives us an extremely powerful set of OO functionality. As SVG is
  464. designed with inheritance in mind, using object subclassing to represent
  465. it is perfectly the right thing to do.
  466.  
  467. But there are also areas where typed object structure would make
  468. things more complex. For example, to implement the copy buffer we had
  469. to save the full state of copied objects. While this could be done
  470. with the separate virtual method of SPObject, we can use a much easier
  471. way and create the duplicate corresponding SPRepr.  As our document
  472. model already has to implement generation of full object
  473. representation for SPRepr tree of nodes, generation of new objects
  474. during paste happens automatically when the given SPRepr is inserted
  475. into XML tree. The agnostic xml tree is also used for undo stack, as
  476. described earlier.
  477.  
  478. The main benefit comes from the extreme simplicity of the XML tree
  479. manipulation API.  All operations can be done, using only around 10
  480. methods, which makes code much more robust, and is perfect for
  481. implementing compatibility sensitive things, like a plugin API.
  482.  
  483. The XML tree also makes implementing two SVG features - cloning and
  484. animations - much easier by providing an invariant backbone.
  485.  
  486.  
  487.  
  488. 22. Novemebr 2002
  489. Lauris Kaplinski
  490. <lauris@kaplinski.com>
  491.